/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.skatiger.jrove.component;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.el.ELException;
import javax.el.ValueExpression;

import org.skatiger.jrove.JroveException;
import org.skatiger.jrove.ViewContext;
import org.skatiger.jrove.renderer.Renderer;

/**
 * 
 * @author skatiger
 * 
 */
public abstract class UIComponent {
	protected Map<String, ValueExpression> bindings;

	public UIComponent() {
	}

	public final void encodeAll(ViewContext context) throws IOException {
		if (context == null) {
			throw new NullPointerException("ViewContext can not be null.");
		}

		if (isRendered()) {
			prepareEncode(context);
			this.encodeBegin(context);

			// rendering children
			if (this.getRendersChildren()) {
				this.encodeChildren(context);
			} else {
				if (this.getChildCount() > 0) {
					for (UIComponent comp : this.getChildren()) {
						comp.encodeAll(context);
					}
				}
			}
			this.encodeEnd(context);
		}
	}

	protected void prepareEncode(ViewContext context) {
		if (context == null) {
			throw new NullPointerException("ViewContext can not be null.");
		}
	}

	public abstract void encodeBegin(ViewContext viewContext)
			throws IOException;

	public abstract void encodeChildren(ViewContext viewContext)
			throws IOException;

	public abstract void encodeEnd(ViewContext viewContext) throws IOException;

	public abstract Map<String, Object> getAttributes();

	protected abstract ViewContext getViewContext();

	// children or facets
	public abstract int getChildCount();

	public abstract List<UIComponent> getChildren();

	public abstract UIComponent getFacet(String name);

	public int getFacetCount() {
		Map<String, UIComponent> facets = getFacets();
		return facets == null ? 0 : facets.size();
	}

	public abstract Map<String, UIComponent> getFacets();

	public abstract Iterator<UIComponent> getFacetsAndChildren();

	public abstract UIComponent getParent();

	public abstract Renderer getRenderer(ViewContext context);

	public abstract void setRendererType(String rendererType);

	public abstract String getRendererType();

	public abstract boolean getRendersChildren();

	public abstract String getClientId(ViewContext context);

	public String getClientId() {
		return getClientId(getViewContext());
	}

	public abstract String getId();

	public abstract boolean isRendered();

	public abstract void setId(String id);

	abstract void setParent(UIComponent parent);

	public abstract void setRendered(boolean rendered);

	private boolean _transient = false;

	public boolean isTransient() {
		return _transient;
	}

	public void setTransient(boolean newTransientValue) {
		this._transient = newTransientValue;
	}

	public ValueExpression getValueExpression(String name) {
		if (name == null) {
			throw new NullPointerException("name can not be null");
		}
		if (bindings != null) {
			return bindings.get(name);
		}
		return null;
	}

	public void setValueExpression(String name, ValueExpression expression) {
		if (name == null) {
			throw new NullPointerException("name");
		}
		if (name.equals("id")) {
			throw new IllegalArgumentException(
					"Can't set a ValueExpression for the 'id' property.");
		}
		if (name.equals("parent")) {
			throw new IllegalArgumentException(
					"Can't set a ValueExpression for the 'parent' property.");
		}

		if (expression == null) {
			if (bindings != null) {
				bindings.remove(name);
				if (bindings.isEmpty()) {
					bindings = null;
				}
			}
		} else {
			if (expression.isLiteralText()) {
				try {
					Object value = expression.getValue(getViewContext()
							.getELContext());
					getAttributes().put(name, value);
					return;
				} catch (ELException e) {
					throw new JroveException(e);
				}
			}
			if (bindings == null) {
				bindings = new HashMap<String, ValueExpression>();
			}
			bindings.put(name, expression);
		}
	}

	public String getContainerClientId(ViewContext context) {
		if (context == null) {
			throw new NullPointerException("ViewContext can not be null.");
		}
		return getClientId(context);
	}

	public UIComponent findParentComponent(Class<? extends UIComponent> type) {
		return _ComponentUtils.findParentComponent(this, type);
	}

	public UIComponent[] findChidlrenComponent(Class<? extends UIComponent> type) {
		return _ComponentUtils.findChidlrenComponent(this, type);
	}

}
